package com.ciei.dpagm.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ciei.dpagm.common.enums.ErrorCodeEnum;
import com.ciei.dpagm.common.enums.LogTypeEnum;
import com.ciei.dpagm.common.service.BaseService;
import com.ciei.dpagm.entity.Log;
import com.ciei.dpagm.entity.Users;
import com.ciei.dpagm.mapper.LogMapper;
import com.ciei.dpagm.util.*;
import eu.bitwalker.useragentutils.UserAgent;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class LogService extends BaseService<LogMapper, Log> {



	/***
	 * 添加日志
	 * @param logType 			日志类型（access：接入日志；save:新增；delete：删除；update：修改；select：查询日志；loginLogout：登录登出；）
	 * @param logInfo 			日志信息(例：系统管理/用户管理/删除)
	 * @param module 			业务模块(例:用户管理)
	 * @param dataBeforeUpdate 	更新前的数据（实体json格式）
	 * @param dataAfterUpdate 	更新后的数据（实体json格式）
	 * @param currentUser   	当前登录用户，为null则从安全框架获取
	 * @author wkh
	 */
	public void addLog(String logType, String logInfo, String module, String dataBeforeUpdate, String dataAfterUpdate, Users currentUser, String objectId) {
		Log log = new Log();
		HttpServletRequest request;
		if (RequestContextHolder.getRequestAttributes() != null) {
			request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
		} else {
			request = null;
		}
		if (currentUser == null && request != null) {
			currentUser = (Users) SecurityUtils.getSubject().getPrincipal();
		}
		log.setLogType(logType);
		log.setLogInfo(logInfo);
		log.setModule(module);
		log.setDataBeforeUpdate(dataBeforeUpdate);
		log.setDataAfterUpdate(dataAfterUpdate);
		if (currentUser != null) {
			log.setOperatorId(currentUser.getUserId());
			log.setOperatorAccount(currentUser.getLoginName());
			log.setOperatorName(currentUser.getUserName());
		}else {
            log.setOperatorName("系统");
        }
		log.setCreateTime(new Date());
		if (request != null) {
			request.getRequestURL();
			log.setRemoteAddress(UrlUtil.getRealIpAddress(request));
			String userAgentStr = request.getHeader("user-agent");
			UserAgent userAgent = UserAgent.parseUserAgentString(userAgentStr);
			log.setBrowserName(userAgent.getBrowser().getName());
		}
		log.setObjectId(objectId);

		save(log);
	}

	/***
	 * 添加日志
	 * @param logType 			日志类型
	 * @param logInfo 			日志信息(例：系统管理/用户管理/删除)
	 * @param module 			业务模块(例:用户管理)
	 * @param dataBeforeUpdate 	更新前的数据（实体json格式）
	 * @param dataAfterUpdate 	更新后的数据（实体json格式）
	 */
	public void addLog(String logType, String logInfo, String module, String dataBeforeUpdate, String dataAfterUpdate) {
		addLog(logType, logInfo, module, dataBeforeUpdate, dataAfterUpdate, null,null);
	}

	/**
	 * 添加日志
	 * @param logType				日志类型
	 * @param logInfo				日志信息
	 * @param module				业务模块
	 * @param dataBeforeUpdate		更新前的数据
	 * @param dataAfterUpdate		更新后的数据
	 * @param currentUser			当前登录用户，为null则从安全框架获取
	 */
	public void addLog(LogTypeEnum logType, String logInfo, String module, String dataBeforeUpdate, String dataAfterUpdate, Users currentUser) {
		addLog(logType.getTypeCode(), logInfo, module, dataBeforeUpdate, dataAfterUpdate, currentUser,null);
	}

	/**
	 * 添加日志
	 * @param logType				日志类型
	 * @param logInfo				日志信息
	 * @param module				业务模块
	 * @param dataBeforeUpdate		更新前的数据
	 * @param dataAfterUpdate		更新后的数据
	 * @param currentUser			当前登录用户，为null则从安全框架获取
	 * @param objectId			    对象id，操作设备存入deviceKey,方便快速搜索
	 */
	public void addLog(LogTypeEnum logType, String logInfo, String module, String dataBeforeUpdate, String dataAfterUpdate, Users currentUser, String objectId) {
		addLog(logType.getTypeCode(), logInfo, module, dataBeforeUpdate, dataAfterUpdate, currentUser,objectId);
	}

	/**
	 * 添加日志
	 * @param logType				日志类型
	 * @param logInfo				日志信息
	 * @param module				业务模块
	 * @param dataBeforeUpdate		更新前的数据
	 * @param dataAfterUpdate		更新后的数据
	 */
	public void addLog(LogTypeEnum logType, String logInfo, String module, String dataBeforeUpdate, String dataAfterUpdate) {
		addLog(logType.getTypeCode(), logInfo, module, dataBeforeUpdate, dataAfterUpdate, null,null);
	}

	public <T> List<Map<String, Object>> compareTwoClass(T class1, T class2) throws IllegalAccessException {
		List<Map<String, Object>> list = new ArrayList<>();
		//获取对象的class
		Class<?> clazz1 = class1.getClass();
		Class<?> clazz2 = class2.getClass();
		//获取对象的属性列表
		Field[] field1 = clazz1.getDeclaredFields();
		Field[] field2 = clazz2.getDeclaredFields();
		//遍历属性列表field1
		for (Field field : field1) {
			//遍历属性列表field2
			for (Field value : field2) {
				//如果field1[i]属性名与field2[j]属性名内容相同
				if (field.getName().equals(value.getName())) {
					field.setAccessible(true);
					value.setAccessible(true);
					//如果field1[i]属性值与field2[j]属性值内容不相同
					if (!compareTwo(field.get(class1), value.get(class2))) {
						Map<String, Object> map2 = new HashMap<>(4);
						map2.put("name", field.getName());
						map2.put("old", field.get(class1));
						map2.put("new", value.get(class2));
						list.add(map2);
					}
					break;
				}
			}
		}

		return list;
	}

	public boolean compareTwo(Object object1, Object object2) {

		if (object1 == null && object2 == null) {
			return true;
		}
		if (object1 == null) {
			return false;
		}
		return object1.equals(object2);
	}

	/**
	 * 获取日志操作类型下拉框数据
	 * @return	日志操作类型下拉框数据
	 */
	public JSONObject loadLogTypeComboOption() {
		LogTypeEnum[] typeEnums = LogTypeEnum.class.getEnumConstants();
		JSONArray options = new JSONArray();
		for (LogTypeEnum typeEnum : typeEnums) {
			JSONObject option = new JSONObject(4);
			option.put("value", typeEnum.getTypeCode());
			option.put("label", typeEnum.getDescription());
			options.add(option);
		}

		return JsonUtil.getSuccess("").fluentPut(APP.DATA, options);
	}

	public JSONObject loadLogTableData(PageParam pageParam) {
		int currentPage = pageParam.getCurrentPage();
		int pageSize = pageParam.getPageSize();
		Map<String, Object> sortFields = pageParam.getSort();
		if (sortFields.isEmpty()) {
			sortFields.put("createTime", "desc");
		}
		String[] inFields = new String[]{"objectId"};
		return loadLogTableData(currentPage, pageSize, pageParam.getParams(), sortFields, inFields);
	}

	/**
	 * 分页查询,根据前端操作账号、操作时间查询
	 *
	 * @param currentPage 当前页码
	 * @param pageSize    当前页条数
	 * @param params      参数条件值
	 * @param sortFields  排序字段
	 * @param inFields	  范围字段
	 */
	public JSONObject loadLogTableData(int currentPage, int pageSize, Map<String, Object> params, Map<String, Object> sortFields, String[] inFields) {
		IPage<Log> page = new Page<>();
		page.setSize(pageSize);
		page.setCurrent(currentPage);
		sortFields = ConvertUtil.humpToLines(sortFields);
		QueryWrapper<Log> wrapper = new QueryWrapper<>();
		if (params != null && !params.isEmpty()) {
			// 操作时间，在startDate和endDate范围内
			if (params.get("startDate") != null) {
				Date startTime = DateUtil.stringToStartOfDate(params.get("startDate").toString());
				wrapper.lambda().ge(Log::getCreateTime, startTime);
			}
			if (params.get("endDate") != null) {
				Date endTime = DateUtil.stringToEndOfDate(params.get("endDate").toString());
				wrapper.lambda().le(Log::getCreateTime, endTime);
			}
			if (params.get("operatorAccount") != null) {
				String operatorAccount = params.get("operatorAccount").toString();
				if (!StringUtils.isBlank(operatorAccount)) {
					wrapper.lambda().like(Log::getOperatorAccount, operatorAccount);
				}
			}
			// 操作类型
			if (params.get("logType") != null && !"".equals(params.get("logType").toString())) {
				wrapper.lambda().eq(Log::getLogType, params.get("logType"));
			}
		}
		if (sortFields != null) {
			for (Map.Entry<String, Object> entry : sortFields.entrySet()) {
				String sortField = ConvertUtil.humpToLine(entry.getKey());
				String order = entry.getValue() == null ? "" : entry.getValue().toString().toLowerCase();
				if (APP.DESC.equals(order)) {
					wrapper.orderByDesc(sortField);
				} else if (APP.ASC.equals(order)) {
					wrapper.orderByAsc(sortField);
				}
			}
		}
		if (inFields != null) {
			for (String inField : inFields) {
				List<Object> inList = ((List<Object>) params.get(inField));
				if (inList != null && inList.size() > 0) {
					wrapper.in(ConvertUtil.humpToLine(inField), inList);
				}
			}
		}
		page(page, wrapper);

		JSONObject jsonObject = new JSONObject();
		JSONArray jsonArray = new JSONArray();
		List<Log> logList = page.getRecords();
		if (logList.size() > 0) {
			for (Log log : logList) {
				JSONObject dataJson = new JSONObject();
				dataJson.put("logId", log.getLogId());
				dataJson.put("operatorAccount", log.getOperatorAccount());
				dataJson.put("operatorName", log.getOperatorName());
				dataJson.put("remoteAddress", log.getRemoteAddress());
				dataJson.put("createTime", log.getCreateTime());
				dataJson.put("logInfo", log.getLogInfo() == null ? "" : log.getLogInfo());
				dataJson.put("operateModule", log.getModule() == null ? "" : log.getModule());
				LogTypeEnum logType = EnumUtil.getEnumObject(LogTypeEnum.class, e -> e.getTypeCode().endsWith(log.getLogType())).orElse(null);
				dataJson.put("logType", logType == null ? "" : logType.getDescription());
				dataJson.put("browserName", log.getBrowserName() == null ? "" : log.getBrowserName());
				dataJson.put("dataBeforeUpdate", log.getDataBeforeUpdate() == null ? "" : log.getDataBeforeUpdate());
				dataJson.put("dataAfterUpdate", log.getDataAfterUpdate() == null ? "" : log.getDataAfterUpdate());
				jsonArray.add(dataJson);
			}
		}
		jsonObject.put(APP.DATA, jsonArray);
		jsonObject.put(APP.TOTAL_COUNT, page.getTotal());
		jsonObject.put(APP.POS, (currentPage - 1) * pageSize);
		jsonObject.put(APP.CURRENT_PAGE, page.getCurrent());
		return JsonUtil.getSuccess("").fluentPut(APP.DATA, jsonObject);
	}
}
